The Problem
The relatively recent and on-going
legal actions
between Cloanto and Hyperion Entertainment
has me concerned about implementing VertigOS as a port of the BOAR Project.
The BOAR Project is a clean-room implementation of a proper subset of AmigaOS.
including only the exec.library
and dos.library
components of the operating system.
While not binary compatible with AmigaOS,
it should be source compatible with a large number of native AmigaDOS console applications,
particularly those which treat BPTRs as opaque references.
(BOAR implements all DOS pointers as APTRs instead of BPTRs.)
BOAR's library and device driver APIs are sufficiently powerful to enable
a significant reproduction of actual AmigaOS libraries (albeit disk-resident)
that, frankly, has me concerned that I might become a future target for litigation.
I was around for the fall of Amiga, circa 2000, for what I like to call the Battle of the Operating Systems. You had AmigaDE, QNX Neutrino, MorphOS, AmigaOS Classic, and I think a few others which I can't remember. Lots of lawsuits, lots of uncertainty in the market, and lots of toxic partisanship in the public forums. Over the years, these have mostly subsided. And, had Cloanto not pressed charges against Hyperion recently, I wouldn't have thought twice about moving forward with porting the BOAR Project. The modernity of Cloanto's and Hyperion's actions, however, motivates my fear.
Requirements for a Replacement Basis
Ideally, the operating system for the Kestrel-3 should meet the following criteria:
- Single-Address Space. The KCP53000 lacks an MMU, as will earlier developments of the KCP53010.
- Discoverable. Resources on how to write software for the platform should be easy to write and/or easy to come by. It should be easy for one person to understand the whole technology stack.
- Easy to port to new platforms. There are lots of small and simple operating systems which are tied to a specific hardware platform. Relatively few are designed with the goal of running on a wide variety of platforms.
- Small. I only have 1MB of RAM available to me on a processor whose minimum instruction size is 4 bytes each. It needs to be small enough to let me bring the system up and still have reasonable amount of resources left over to run interesting applications. Note that, due to performance reasons, I cannot rely on ROM resources while the OS is running (except as a ROM-disk).
- Expandable. The guts of the operating system should allow for future growth. If I mount a volume with a foreign filesystem, I should be able to treat it like any other after installing a driver. When a GUI becomes available, it should be effortless to include it in the system image, or to use it in an application. When an MMU becomes available, it should be possible to make use of it in a meaningful way, etc.
- Simple. Small size and simplicity go hand in hand. You really can't get any more simpler than CP/M. However, for only a modest increase in the number of system calls supported, Tripos offers a lot more bang for the buck while still being an operating system simple enough that a single individual can fully understand it.
- Unencumbered. The probability of a company taking legal actions against me for using an operating system that resembles their own should be as vanishingly small as possible. Ideally, zero.
- Practical. Writing real-world applications should not require me to patch the kernel or supporting libraries into some degree of maturity.
Now let's look at some existing alternatives:
- No-MMU Linux. Before you ask, no. It won't fit comfortably in the 1MB of address space of the headless Kestrel-3. The lack of MMU support also severely restricts what you can actually run, limiting practicality. That nearly all Linux applications in user-land over-allocate (a technique that only makes sense with MMU-based systems) also severely curtails how long you can run some programs. It's also extremely hard for a single person to port (requires porting entire development toolchain first, then the kernel itself, then the user-space: commands, package managers, etc.; a multi-man-year effort, easily. Proof: look how long the RISC-V community ported Linux to its platform, and that was considered fast!).
- Minix. Modern versions depends entirely on the presence of an MMU. Earlier versions might work, but resources are hard to come by. Almost certainly as hard to port as Linux is.
- Lunix, A/65, GEOS/64, et. al. Too specific to the 6502 or 65816 platform.
- CP/M. Small, simple, and single-address space, but not very expandable without resorting to terrible hacks such as "terminate and stay resident" programs. At least it is GPLed though!
- TRON. You'd think there'd be more details about B-TRON, et. al. online; but, there's nothing. Not discoverable.
- Tripos. While not the smallest possible OS that a person can run, it does run comfortably in a small, single address space (consider, original Amiga 1000s had only 256KB of RAM installed). It only has 22 kernel calls and 32 DOS library calls, making it relatively easy for a single person to port it from scratch in assembly language if necessary. Programming information is still discoverable (at least for the 68000 version) online. It is expandable along multiple dimensions, including support for installable filesystems, installable device drivers, etc. And, it is practical: while it may not enjoy the conveniences of a modern Unix or Plan-9 environment, nonetheless it once had a rich development ecosystem (especially on the Amiga, where several compilers and interpreters could be found working together), was host to excellent productivity tools (the Vim text editor was first written for AmigaDOS!), etc. Despite the pro-MMU nay-sayers, Tripos has never been a toy.
Pure Tripos: A Viable Work-Around?
Clearly, Tripos is clearly the closest thing to a sweet spot there is. That basically just leaves only the legal status of Tripos as my greatest unknown: I don't actually know the official legal status of Tripos. In fact, nobody actually seems to know anymore. However, I do know that many independent recreations of it has been made over the years. There's AROS, AmigaOS itself of course, MorphOS, and less Amiga-dependent, Cintpos. Wikipedia says that there is still commercial support for a now-Linux-hosted version of Tripos, but there is literally no details or supporting evidence to back this claim. I don't foresee problems by creating an independent and open source fork of Tripos.
For these reasons, I've decided to move VertigOS away from its AmigaOS underpinnings (via the BOAR Project) and towards unadulterated Tripos itself. I've not observed any commercial interest in Tripos since Metacomco owned the rights, and as well, Tripos' own inventor, Dr. Martin Richards, has invested some work in recreating Tripos to run hosted under a POSIX operating system. Ergo, with prior art for independent recreations existing, I feel much safer pursuing Tripos as a viable foundation for VertigOS than I do with AmigaOS.
Opportunities
Base Architecture and Easy Programming API
As it happens,
this might end up being a better basis for the operating system anyway.
According to the programmer's reference guide,
the 68000 version of the Tripos kernel supports only 22 system calls.
This is far smaller than even the most basic version of exec.library
.
The Tripos kernel is technically less capable than the AmigaOS kernel;
however, in practical terms, they're still quite close to each other.
Its calling convention is also simpler,
dependent upon the 68K TRAP
instruction
instead of magic memory addresses and jump tables.
For RISC-V purposes, we would use the ECALL
instruction instead.
For the 68000 version of Tripos,
the DOS library has nearly identical structure to exec
-style libraries,
and is called exactly the same way (right down to offsets being labeled with _LVO
prefixes).
I would not be surprised if there was some cross-pollination of ideas between exec
and Tripos.
Unlike exec
libraries, however,
there does not exist any kind of protocol for opening, closing, or expunging libraries.
To gain access to the DOS library,
a client would invoke the kernel call, FindDOS
.
The result
(which always succeeds, so no need for error checking)
is a pointer to the library base, from which you can make calls via LVOs.
The following assembly listing
sketches some RISC-V code that illustrates how VertigOS might be invoked
on a Kestrel-3:
; Find our DOS library
;
addi a0,SC_FINDDOS
ecall
addi s0,a0,0 ; Assign DOSBase to S0
;
; Open the RAM:Foo/Bar file
;
L1: auipc s1,0 ; S1 -> L1
ld a0,Filename-L1(s1) ; A0 -> filename
addi a1,x0,MODE_OLDFILE ; A1 = open a hopefully existing file
ld t0,_LVOOpen(s0) ; Invoke the Open call
jalr ra,0(t0)
sd a0,result1-L1(s1) ; Save our result and dispatch if it worked
bne a0,x0,success
ld t0,_LVOIoErr(s0) ; Otherwise, find out *why* our attempt failed
jalr ra,0(t0)
sd a0,result2-L1(s1)
jal x0,printErrorMsg ; Print an error message, and exit.
; ...etc...
align 8
Filename:
dword *+8
byte "RAM:Foo/Bar",0
result1:
dword 0
result2:
dword 0
This illustrates how one would call the kernel (via ecall
)
as well as how one would invoke the DOS API (via a call to an LVO).
It's not clear to me that I need to preserve these two calling conventions. Cintpos, Dr. Richards' most recent system, implements its kernel almost entirely in BCPL and uses the global vector to export its functionality. That is, the DOS and the kernel have uniform calling interfaces: the global vector. Still, I'll aim to keep the distinction as long as it doesn't become too onerous. Again, it's a proven architecture, and despite wanting to build a computer all my own, I'm not in the business of reinventing everything at once. ;-)
Shared Libraries
As I mentioned in my blog article on ELF, Tripos supported a kind of statically-compiled, shared library facility which shares a lot with Posix-style shared object libraries, but without all the janky complexity. AmigaDOS never supported this feature, but you better believe that a pure Tripos environment absolutely did, and I have full intentions of supporting this feature as well.
I intend on using this library mechanism to support a native graphical user interface, support networking, and include other facilities which I need in order to meet Ken's Challenge.
Memory Protection
Tripos, and indeed the first versions of VertigOS, will run in a single, flat, physical address space. OR, if not physical, then at least a single, flat, virtualized address space. Eventually, I'd like to try my hand at expanding its horizons and moving it into the multi-address space realm.
I have no defined plan for supporting this. However, I do know that a kernel which offers only 22 system calls and a DOS with only 32 system calls, will be vastly easier to adapt than a kernel with hundreds for each.
The existence of the FindDOS
system call,
ironically,
just might be the ticket that makes the whole thing work.
I foresee the use-case where a program kicks off a new domain ("address space"),
loads code into it, and starts it running as a background process.
Alternatively, the operating system will allow a user to select which executables it trusts or doesn't trust,
such that untrusted binaries are loaded/executed in their own domain.
Since an application must call FindDOS
to acquire a reference to the DOS API,
this gives the kernel a chance to map whatever dependencies are required by the application
into this new domain before the rest of the program has a chance to depend upon them.
IPC will always be a sticky point, of course;
you can't use QPkt
to send messages to different domains, for example.
(Nearly all of Tripos messages have pointers baked into them,
and the operation of QPkt
depends heavily upon this fact.)
However, if each address space basically gets its own private copy of Tripos,
and if there exists a special handler to basically implement cross-domain sockets,
normal calls to Read
and Write
would then be all you needed for full IPC between domains.